<?php
/**
 * Copyright (c) 2006-2008, Julien PORTALIER
 * http://featherphp.googlecode.com/
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 */

/**
 * Webservices Object Mapping.
 * 
 * \code
 * class User extends ActiveResource_Base
 * {
 *   protected $_site     = 'http://www.domain.tld';
 *   protected $_path     = '/user';
 *   protected $_resource = 'User';
 * }
 * \endcode
 */
abstract class ActiveResource_Base extends Object
{
	protected $id;
	
	protected $_site;
	protected $_path;
	protected $_resource;
	
	protected $_extension = 'json';
	protected $_format    = 'application/json';
	protected $_headers   = array();
	
	protected $_auth_protocol = null;
	protected $_auth_username;
	protected $_auth_password;
	
	function __construct($id_or_data=null)
	{
		$this->_name = get_class($this);
		
		if (empty($this->_resource))
			$this->_resource = $this->_name;
		
		if (empty($this->_path))
			$this->_path = Inflector::tableize($this->_resource);
		
		# HTTP headers
#		$this->_headers['Content-Type'] = $this->_format;
		$this->_headers['Accept']       = $this->_format;
		
		# data
		if (!empty($id_or_data))
		{
			if (is_array($id_or_data)) {
				$this->set($id_or_data);
			}
			else {
				$this->read($id_or_data);
			}
		}
	}
	
	function __get($attr)
	{
		if ($attr == '_request')
		{
			Load::_include('models', 'request');
			$this->_request = new ActiveResource_Request($this->_headers);
			if ($this->_auth_protocol !== null)
			{
				$this->_request->set_authentication($this->_auth_protocol,
					$this->_auth_username, $this->_auth_password);
			}
			return $this->_request;
		}
		return parent::__get($attr);
	}
	
	function set(array &$data)
	{
		$this->_data = $data;
	}
	
	
	// CRUD with REST
	
	function read($id=null, $fields=null)
	{
		list($code, $data) = $this->_request->run('GET', $this->geturl($id ? $id : $this->id));
		if ($code != 200) {
			throw new ActiveResource_Exception($code);
		}
		$data = $this->decode_data($data);
		$this->set($data);
	}
	
	function create($data=null, $validate=true)
	{
		$data = $this->encode_data($data);
		list($code, $data) = $this->_request->run('POST', $this->geturl(), &$data);
		if ($code != 201) {
			throw new ActiveResource_Exception($code);
		}
	}
	
	function update($id, $data=null, $validate=true)
	{
		$data = $this->encode_data($data);
		list($code, $data) = $this->_request->run('PUT', $this->geturl($id ? $id : $this->id), &$data);
		if ($code != 200) {
			throw new ActiveResource_Exception($code);
		}
	}
	
	function delete($id=null)
	{
		list($code, $data) = $this->_request->run('DELETE', $this->geturl($id ? $id : $this->id));
		if ($code != 200 or $code != 204) {
			throw new ActiveResource_Exception($code);
		}
	}
	
	
	function save($validate=true, $type=null)
	{
		if ($type == ':create' or empty($this->id)) {
			return $this->create(null, $validate);
		}
		return $this->update($this->id, null, $validate);
	}
	
	/**
	 * @todo ?
	 */
	function & find()
	{
		
	}
	
	
	// INTERNALS
	
	protected function geturl($id=null)
	{
		$url  = $this->_site.'/';
		$url .= empty($id) ? $this->_path : Inflector::singularize($this->_path).'/'.$id;
		$url .= empty($this->_extension) ? '' : '.'.$this->_extension;
		return preg_replace(':(?<!http\:|https\:)[/]+:', '/', $url);
	}
	
	protected function & encode_data(array $data=null)
	{
		if (empty($data))
			$data = $this->_data;
		
		$postfields = array();
		switch($this->_format)
		{
			case 'application/json':
				$postfields['json'] = json_encode($data);
				break;
			
			case 'text/xml': 
				Load::libs('xml');
				$postfields['xml'] = xml::transform($data, $this->_resource);
				break;
			
			default:
				trigger_error("Unknown mimetype: {$this->_format}", E_USER_ERROR);
		}
		return $postfields;
	}
	
	protected function & decode_data($data)
	{
		switch($this->_format)
		{
			case 'application/json':
				$data = json_decode($data, true);
				break;
			
			case 'text/xml': 
				Load::libs('xml');
				$data = xml::from_string($data);
				break;
			
			default:
				trigger_error("Unknown mimetype: {$this->_format}", E_USER_ERROR);
		}
		return $data;
	}
	
	function & get_associations()
	{
		$associations = array();
		return $associations;
	}
}
?>